Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move AragonApp to unstructured storage #376

Merged
merged 11 commits into from
Aug 22, 2018
Merged

Move AragonApp to unstructured storage #376

merged 11 commits into from
Aug 22, 2018

Conversation

bingen
Copy link
Contributor

@bingen bingen commented Jul 30, 2018

Fixes #342

TODO:

  • Do the same for Kernel
  • Adapt dao kits and apps if needed

@coveralls
Copy link

coveralls commented Jul 31, 2018

Coverage Status

Coverage increased (+0.02%) to 99.028% when pulling 65ce93a on unstructured_storage into 7090b7c on dev.

@bingen bingen changed the title [WIP] Move AragonApp to unstructured storage Move AragonApp to unstructured storage Jul 31, 2018
Copy link
Contributor

@izqui izqui left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My main feedback is that the fact that we are using unstructured storage shouldn't change how one interacts with the contract. It is just an implementation detail that should be abstracted away.

contract AppStorage is UnstructuredStorage {
// keccak256("IKernel.kernel")
bytes32 public constant kernelPosition = 0xe30296d9191ef86f01c8532636453e486db506a7be081a32b88c263fbc3a5e15;
// keccak256("bytes32.appIdPosition")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be bytes32.appId for consistency with other names.

uint256[95] private storageOffset; // forces App storage to start at after 100 slots
uint256 private offset;
contract AppStorage is UnstructuredStorage {
// keccak256("IKernel.kernel")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would remove the type from the key names as types can change in the future and they are all storing a 32-byte word anyway.

It would make sense to have the type be explicit if we were storing more complex data types as arrays, mappings or structs, but for simple values I would remove it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also add an aragonOS.appStorage prefix to all keys to remove the possibility of collisions.


contract UnstructuredStorage {
// keccak256("uint256.initializationBlock"), used by Initializable
bytes32 public constant initializationBlockPosition = 0xd9120073d934f0c287a3a735b193740486dbba88578fd8f3cbb2b9a9b654e7ce;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not move to the Initializable contract directly? Same comment on the naming of the key as for the other storage values in AppStorage.

_;
}

modifier isInitialized {
require(initializationBlock > 0);
require(getStorageUint256(initializationBlockPosition) > 0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should use getInitializationBlock()

// keccak256("bytes32.appIdPosition")
bytes32 public constant appIdPosition = 0x71ca50ba614d1d1a9a2433a5d0187a13e1a8c20bbe0013968dd089c71fd760eb;
// keccak256("address.pinnedCode"), used by Proxy Pinned
bytes32 public constant pinnedCodePosition = 0x0e2081dcdbb92be1037e55b39e913e70697c33a8a8b9bb01110e6f9e576089b8;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the storage positions are an implementation detail that doesn't need to be exposed in the public API of the contract.

For all values I would add a getter and setter so the fact that AppStorage is using UnstructuredStorage is abstracted away from people interacting with the contract or deriving it:

function kernel() public view returns (IKernel) {
  return IKernel(getStorageAddress(kernelPosition));
}

function setKernel(IKernel newKernel) internal {
  setStorageAddress(kernelPosition, address(_kernel));
}

@bingen bingen force-pushed the unstructured_storage branch 3 times, most recently from 1cea212 to a83d751 Compare August 2, 2018 20:20
@bingen bingen force-pushed the unstructured_storage branch from a83d751 to 5766201 Compare August 2, 2018 20:39
Copy link
Contributor

@izqui izqui left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! Just some comments related with the tests mainly.

Let's wait for @sohkai's review before merging though

@@ -9,7 +9,7 @@ import "./IEVMScriptExecutor.sol";
import "./IEVMScriptRegistry.sol";

import "../apps/AppStorage.sol";

import "../kernel/IKernel.sol";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to import

@@ -0,0 +1,61 @@
const getContract = name => artifacts.require(name)

contract('Constants', accounts => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'Constants' -> 'Unstructured storage'

kernel = await getContract('Kernel').new()
})

it('test', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would split into one test case per value being checked

//checks
assert.equal(
parseInt(await web3.eth.getStorageAt(app.address, (await app.getInitializationBlockPosition())), 16),
(await app.getInitializationBlock.call()).toString(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can check what the block number was after app.initialize() and assert equality with the value stored at the storage position.


//checks
assert.equal(
parseInt(await web3.eth.getStorageAt(app.address, (await app.getInitializationBlockPosition())), 16),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would get the positions from test/mocks/KeccakConstants.sol rather than from 'AragonApp', even though that equality already tested in test/keccak_constants.js. Would allow to remove all the position public getters from AppStubStorage.

Copy link
Contributor Author

@bingen bingen Aug 3, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, but if we remove the public getters from AppStubStorage then tests in keccak_constants.js won't work. Here I see we are testing 2 things:

  • That hardcoded position is what it is intended to be (not a big deal, but better to have it, to avoid collisions)
  • That things are properly stored at those positions.

Testing the second one, the first one would be implicitly tested if we use positions from KeccaKConstants but I think it's better to have both for 2 reasons: 1) in case of failing, it's eaiser to know what the problem is, 2) we could be pointing at another position that has the same value (yes, unlikely, but still) and having both tests we would have more chances to catch it.
(And AppSutbStorage is just for testing, so no big deal having those getters there)

Address PR 376 comments.
Copy link
Contributor

@sohkai sohkai left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👏 Really loving this, and the tests :D.

Have a few comments that I'll address :).

setStorageAddress(pinnedCodePosition, _pinnedCode);
}

function pinnedCode() internal view returns (address) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the pinnedCode could go into AppProxyPinned, since it's only relevant there.

pragma solidity ^0.4.18;


contract UnstructuredStorage {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we could make this a library, and remove all public functions from it (I don't think there's a big benefit in having the getters be public).

@@ -36,7 +36,7 @@ module.exports = function (deployer, network, accounts, arts = null) {
console.log('Deployed APM at:', apmAddr)

const apm = getContract('APMRegistry').at(apmAddr)
console.log('Kernel:', await apm.kernel())
console.log('Kernel:', await apm.kernel.call())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I think these .call()s are unnecessary; I'll try removing them.

Copy link
Contributor Author

@bingen bingen Aug 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, they are for testrpc but I had some problems without them using geth on our dao-kits, so now I'm putting them whenever I remember, so in case we finally move our tests to geth they will work.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, weird, this should be handled by truffle (truffle-contract changes its interface based on whether the function is marked constant or not), rather than a testrpc / geth issue.

@sohkai
Copy link
Contributor

sohkai commented Aug 16, 2018

This change should be transparent to the DAO Kits and apps.

@sohkai
Copy link
Contributor

sohkai commented Aug 21, 2018

For deployment cost:

                              CODE DEPOSIT COST    DEPLOYED BYTES     INITIALIZATION BYTES
ACL.json                      66600 more gas       +333               0
APMRegistry.json              367800 more gas      +1839              0
AppProxyFactory.json          307800 more gas      +1539              0
AppProxyPinned.json           102000 more gas      +510               +346
AppProxyUpgradeable.json      88600 more gas       +443               +240
AppStorage.json               72600 more gas       +363               0
AragonApp.json                61800 more gas       +309               0
ENSSubdomainRegistrar.json    70800 more gas       +354               0
EVMScriptRegistry.json        72600 more gas       +363               0
EVMScriptRegistryFactory.json 307800 more gas      +1539              +363
EVMScriptRunner.json          65600 more gas       +328               0
Initializable.json            27400 more gas       +137               0
Kernel.json                   348400 more gas      +1742              0
Repo.json                     61600 more gas       +308               0

Ouch :(.

I'd say this change is worth it though.

* Fix tests after linting

* Remove unnecessary .call() in tests and migrations

* Make UnstructuredStorage a library

* Move pinnedCode storage only to AppProxyPinned
@sohkai
Copy link
Contributor

sohkai commented Aug 21, 2018

Current deployment costs:

                              CODE DEPOSIT COST    DEPLOYED BYTES     INITIALIZATION BYTES
ACL.json                      19200 more gas       +96                0
APMRegistry.json              235200 more gas      +1176              0
AppProxyFactory.json          225000 more gas      +1125              0
AppProxyPinned.json           57600 more gas       +288               +375
AppProxyUpgradeable.json      40800 more gas       +204               +258
AppStorage.json               19600 more gas       +98                0
AragonApp.json                11800 more gas       +59                0
ENSSubdomainRegistrar.json    23400 more gas       +117               0
EVMScriptRegistry.json        25200 more gas       +126               0
EVMScriptRegistryFactory.json 225000 more gas      +1125              +126
EVMScriptRunner.json          12600 more gas       +63                0
Initializable.json            30000 less gas       -150               -1
Kernel.json                   209800 more gas      +1049              0
Repo.json                     11600 more gas       +58                0

@sohkai sohkai merged commit 1ee632b into dev Aug 22, 2018
@sohkai sohkai deleted the unstructured_storage branch August 22, 2018 13:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants